/* C.Stat: Check file status */

#include "kernel.h"

#include "sys/time.h"
#include "sys/stat.h"

#define WRITE_CAT       1
#define READ_CAT        5
#define OS_GetEnv       16

#define UG_READ         ( (S_IREAD)  | (S_IREAD >> 3)  )
#define UG_WRITE        ( (S_IWRITE) | (S_IWRITE >> 3) )
#define OTHER_READ      ( S_IREAD  >> 6 )
#define OTHER_WRITE     ( S_IWRITE >> 6 )
#define ALL_EXEC        ( (S_IEXEC)  | (S_IEXEC >> 3)  | (S_IEXEC >> 6)  )

int stat (char *name, struct stat *buf)
{
        int type;
        int mode = 0;
        unsigned char *p;
        _kernel_osfile_block osfile;
        _kernel_swi_regs regs;

        /* ----- Get file catalogue info ----- */

        type = _kernel_osfile(READ_CAT,name,&osfile);

        if ( type <= 0 )
                return -1;

        buf->st_nlink   = 1;
        buf->st_uid     = 0;
        buf->st_gid     = 0;
        buf->st_size    = (unsigned) osfile.start;  /* file length */
        buf->st_load    = osfile.load;              /* load address */
        buf->st_exec    = osfile.exec;              /* execution address */
        buf->st_attribs = osfile.end;               /* file attributes */

        buf->st_stamp   = ( ( osfile.load & 0xFFF00000 ) == 0xFFF00000 );

        /* ----- Determine file access mode ----- */

        if ( type == 2 )                /* is it a directory? */
                mode |= S_IFDIR;
        else
                mode |= S_IFREG;

        if ( buf->st_attribs & S_AREAD )
                mode |= UG_READ;

        if ( buf->st_attribs & S_AWRITE )
                mode |= UG_WRITE;

        if ( buf->st_attribs & (S_AREAD << 4) )
                mode |= OTHER_READ;

        if ( buf->st_attribs & (S_AWRITE << 4) )
                mode |= OTHER_WRITE;

        mode |= ALL_EXEC;               /* Always allow execute */

        buf->st_mode = mode;

        /* ----- Determine file time stamp ----- */

        if ( buf->st_stamp )
        {
                buf->st_time.t[0] = osfile.exec       & 0xFF;
                buf->st_time.t[1] = osfile.exec >> 8  & 0xFF;
                buf->st_time.t[2] = osfile.exec >> 16 & 0xFF;
                buf->st_time.t[3] = osfile.exec >> 24 & 0xFF;
                buf->st_time.t[4] = osfile.load       & 0xFF;
                buf->st_type      = osfile.load >> 8  & 0xFFF;
        }
        else
        {
                /* File not timestamped - use program start time */

                if ( _kernel_swi(OS_GetEnv,&regs,&regs) != 0 )
                        return -1;

                p = (unsigned char *)regs.r[2];

                buf->st_time.t[0] = p[0];
                buf->st_time.t[1] = p[1];
                buf->st_time.t[2] = p[2];
                buf->st_time.t[3] = p[3];
                buf->st_time.t[4] = p[4];
                buf->st_type      = 0;
        }

        buf->st_mtime = mktime(TIMEstruct(buf->st_time));

        return 0;
}

int set_stat (char *name, struct stat *buf)
{
        _kernel_osfile_block osfile;

        if ( buf->st_stamp )
        {
                osfile.load = 0xFFF00000
                            | buf->st_type << 8
                            | buf->st_time.t[4];
                osfile.exec = buf->st_time.t[3] << 24
                            | buf->st_time.t[2] << 16
                            | buf->st_time.t[1] << 8
                            | buf->st_time.t[0];
        }
        else
        {
                osfile.load = buf->st_load;
                osfile.exec = buf->st_exec;
        }

        osfile.end  = buf->st_attribs & 0x08;         /* locked bit */

        if ( buf->st_mode & S_IREAD )                 /* read bit */
                osfile.end |= 0x01;
        if ( buf->st_mode & S_IWRITE )                /* write bit */
                osfile.end |= 0x02;

        if ( _kernel_osfile(WRITE_CAT,name,&osfile) < 0 )
                return -1;
        else
                return 0;
}
